3. 도커 개요1
2.1. 도커와 Build, Ship, Run
도커의 핵심 작업 흐름
Build, Ship, Run 개념:
도커 작업 흐름:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[Build - 이미지 작성]
│
├─ Dockerfile 작성
├─ 컨텍스트 준비
└─ 이미지 빌드
↓
[Ship - 이미지 배포]
│
├─ 레지스트리에 Push
├─ 이미지 공유
└─ 다른 환경에서 Pull
↓
[Run - 컨테이너 실행]
│
├─ 이미지로 컨테이너 생성
├─ 격리된 실행 환경 구성
└─ 애플리케이션 실행
핵심:
→ Build: 한 번 작성
→ Ship: 어디든 배포
→ Run: 동일하게 실행
용어 정리
- Build: 애플리케이션과 실행 환경을 컨테이너 이미지로 패키징하는 과정
- Ship: 빌드된 이미지를 레지스트리에 업로드하고 다른 환경으로 배포하는 과정
- Run: 이미지로부터 컨테이너를 생성하고 실행하는 과정
- 컨테이너 이미지 (Container Image): 애플리케이션과 그 실행에 필요한 모든 것을 포함한 읽기 전용 패키지
컨테이너 이미지란?
컨테이너 이미지는 컨테이너 형태로 실행하고 싶은 애플리케이션의 구성 요소를 모두 포함한 패키지
컨테이너 이미지 구성:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[컨테이너 이미지]
│
├─ 애플리케이션 바이너리
│ └─ 실행 파일, 스크립트
│
├─ 의존 파일
│ └─ 라이브러리, 설정 파일
│
├─ 실행 환경 설정
│ └─ 환경 변수, 시작 명령어
│
└─ 루트 파일시스템
└─ /bin, /lib, /usr 등
특징:
→ 읽기 전용 템플릿
→ 레이어 구조
→ 재사용 가능
2.1.1. Build: 컨테이너 이미지 작성
컨테이너 이미지 빌드 개념
이미지 빌드 = 컨테이너의 재료 준비
이미지 빌드 프로세스:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[입력]
│
├─ Dockerfile
│ └─ 컨테이너 작성 절차를 기록한 파일
│
└─ 컨텍스트 (Context)
└─ 빌드에 사용할 파일들
├─ 애플리케이션 소스 코드
├─ 설정 파일
└─ 스크립트
↓
[도커 빌드 엔진]
│
├─ Dockerfile 명령어 실행
├─ 컨텍스트 파일 복사
└─ 레이어별 이미지 생성
↓
[출력]
│
└─ 컨테이너 이미지
└─ 모든 구성 요소가 통합된 패키지
용어 정리
- Dockerfile: 컨테이너 이미지를 빌드하기 위한 명령어들을 담은 텍스트 파일
- 컨텍스트 (Context): 빌드 시 Docker 데몬에 전송되는 파일 집합. 소스 코드, 설정 파일 등 포함
- 빌드 엔진: Dockerfile을 해석하고 이미지를 생성하는 Docker 내부 컴포넌트
- 레이어 (Layer): 이미지를 구성하는 파일시스템의 변경 단위. 각 명령어가 새 레이어 생성
실습: Hello World 이미지 빌드
1. 프로젝트 구조 준비:
프로젝트 디렉토리 구조:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
myimage/
├─ hello.sh (실행할 스크립트)
└─ Dockerfile (빌드 명령 파일)
2. 파일 생성 (PowerShell):
# 실습 폴더를 생성하고, 이동하여 진행하는 것을 권장
# 디렉토리 생성
mkdir myimage
# hello.sh 스크립트 작성
$hello = "#!/bin/bash`nset -eu`necho `"Hello, World!`"`nexec sleep infinity`n"
[System.IO.File]::WriteAllText("$PWD\myimage\hello.sh", $hello)
# Dockerfile 작성
@"
FROM ubuntu:22.04
COPY ./hello.sh /hello.sh
RUN chmod +x /hello.sh
ENTRYPOINT ["/hello.sh"]
"@ | Set-Content myimage\Dockerfile -NoNewline
3. Dockerfile 구조 이해:
용어 정리:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[베이스 이미지 (Base Image)]
│
└─ 새 이미지의 기반이 되는 이미지
FROM 명령어로 지정
[컨텍스트 (Context)]
│
└─ 빌드 시 Docker 데몬에 전송되는 파일 집합
docker build 명령의 마지막 인자로 지정하는 경로
[chmod]
│
└─ Linux 파일 권한 변경 명령어
+x는 실행 권한(execute) 부여
[ENTRYPOINT]
│
└─ 컨테이너 시작 시 항상 실행되는 명령
CMD와 달리 덮어쓰기 어려움
Dockerfile 명령어 설명:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
FROM ubuntu:22.04
└─ 베이스 이미지 지정
└─ Ubuntu 22.04를 기반으로 시작
COPY ./hello.sh /hello.sh
└─ 컨텍스트에서 파일 복사
└─ 로컬의 hello.sh를 컨테이너의 /hello.sh로 복사
RUN chmod +x /hello.sh
└─ 빌드 시 명령어 실행
└─ hello.sh에 실행 권한 부여
ENTRYPOINT ["/hello.sh"]
└─ 컨테이너 시작 시 실행할 명령어
└─ 컨테이너 실행 시 hello.sh 자동 실행
용어 정리
- FROM: 베이스 이미지를 지정하는 Dockerfile 명령어. 모든 Dockerfile의 첫 명령어
- COPY: 컨텍스트의 파일을 이미지로 복사하는 명령어
- RUN: 빌드 시점에 명령어를 실행하여 새 레이어 생성. 패키지 설치 등에 사용
- ENTRYPOINT: 컨테이너 시작 시 항상 실행되는 명령어. CMD와 달리 덮어쓰기 어려움
- 베이스 이미지 (Base Image): 새 이미지의 기반이 되는 이미지. FROM으로 지정
- chmod +x: Linux에서 파일에 실행 권한을 부여하는 명령어
4. 이미지 빌드 실행:
docker build -t myimage:v1 ./myimage
빌드 명령어 구조:
docker build 명령어 분석:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
docker build -t myimage:v1 ./myimage
│ │ │ │ │
│ │ │ │ └─ 빌드 컨텍스트 경로
│ │ │ │
│ │ │ └─ 태그 (버전)
│ │ │
│ │ └─ 이미지 이름 (리포지터리)
│ │
│ └─ 태그 옵션
│
└─ 빌드 명령어
결과:
→ 이미지 이름: myimage
→ 태그: v1
→ 전체 이름: myimage:v1
빌드 과정 상세:
도커 빌드 실행 흐름:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[1단계] 컨텍스트 전송
↓
./myimage 디렉토리 내용을
도커 데몬으로 전송
[2단계] Dockerfile 파싱
↓
FROM ubuntu:22.04
├─ ubuntu:22.04 이미지 다운로드
└─ 베이스 레이어 생성
[3단계] 명령어 실행
↓
COPY ./hello.sh /hello.sh
├─ 새 레이어 생성
└─ hello.sh 파일 추가
RUN chmod +x /hello.sh
├─ 새 레이어 생성
└─ 실행 권한 변경
ENTRYPOINT ["/hello.sh"]
└─ 메타데이터에 시작 명령어 저장
[4단계] 이미지 생성
↓
모든 레이어를 합쳐
최종 이미지 생성
[5단계] 태깅
↓
myimage:v1 태그 부여
이미지 레이어 구조
도커 이미지의 레이어 시스템:
이미지 레이어 구조:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[myimage:v1]
│
├─ 레이어 4: ENTRYPOINT 메타데이터
│ (읽기 전용)
│
├─ 레이어 3: chmod +x /hello.sh
│ (읽기 전용)
│
├─ 레이어 2: hello.sh 파일 추가
│ (읽기 전용)
│
└─ 레이어 1: ubuntu:22.04 베이스
(읽기 전용)
장점:
✓ 효율적 저장: 공통 레이어 재사용
✓ 빠른 빌드: 변경된 레이어만 재생성
✓ 캐시 활용: 이전 빌드 결과 재사용
레이어 캐싱 예시:
캐시 활용 메커니즘:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[첫 빌드]
FROM ubuntu:22.04 → 다운로드
COPY ./hello.sh /hello.sh → 복사
RUN chmod +x /hello.sh → 실행
시간: 30초
[두 번째 빌드 - 변경 없음]
FROM ubuntu:22.04 → 캐시 사용
COPY ./hello.sh /hello.sh → 캐시 사용
RUN chmod +x /hello.sh → 캐시 사용
시간: 1초
[hello.sh 수정 후 빌드]
FROM ubuntu:22.04 → 캐시 사용
COPY ./hello.sh /hello.sh → 재실행 (변경됨)
RUN chmod +x /hello.sh → 재실행 (이전 단계 변경)
시간: 5초
최적화 팁:
→ 자주 변경되는 파일은 Dockerfile 하단에 배치
→ 의존성 설치는 상단에 배치
용어 정리
- 레이어 캐싱 (Layer Caching): 이전 빌드의 레이어를 재사용하여 빌드 속도를 높이는 기법
- 태그 (Tag): 이미지의 버전을 식별하는 레이블. 예: myimage:v1에서 v1이 태그
- 리포지터리 (Repository): 동일한 이름의 이미지들을 모아둔 저장소. 태그로 버전 구분
- -t 옵션: docker build에서 이미지 이름과 태그를 지정하는 옵션
2.1.2. Run: 컨테이너 실행
컨테이너 실행 기본
이미지에서 컨테이너 생성:
컨테이너 실행 과정:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[이미지: myimage:v1]
│ (읽기 전용 템플릿)
↓
[docker run 명령어]
↓
[컨테이너 생성]
│
├─ 쓰기 가능 레이어 추가
├─ 네트워크 설정
├─ 격리된 환경 구성
└─ ENTRYPOINT 실행
↓
[실행 중인 컨테이너]
│
└─ PID 1: /hello.sh
└─ "Hello, World!" 출력
└─ sleep infinity 실행
컨테이너 실행 명령어:
docker run --name mycontainer myimage:v1
명령어 구조:
docker run 명령어 분석:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
docker run --name mycontainer myimage:v1
│ │ │ │
│ │ │ └─ 이미지:태그
│ │ │
│ │ └─ 컨테이너 이름
│ │
│ └─ 이름 지정 옵션
│
└─ 실행 명령어
실행 결과:
→ 컨테이너 이름: mycontainer
→ 출력: "Hello, World!"
→ 상태: sleep infinity 대기 중
컨테이너 내부 확인
exec 명령어로 컨테이너 접속:
# 컨테이너 내부 쉘 실행
docker exec -it mycontainer /bin/bash
exec 명령어 옵션:
docker exec 옵션 설명:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
docker exec -it mycontainer /bin/bash
│ │ │ │
│ │ │ └─ 실행할 명령어
│ │ │
│ │ └─ 컨테이너 이름
│ │
│ └─ TTY 할당 (터미널)
│
└─ 인터랙티브 모드
-i (--interactive): 표준 입력 유지
-t (--tty): 가상 터미널 할당
컨테이너 내부 확인 작업:
# 1. 루트 디렉토리 확인
ls /
# 출력 예시:
bin boot dev etc hello.sh home lib ...
↑
hello.sh가 /에 존재
# 2. 실행 중인 프로세스 확인
ps -Ao pid,cmd
# 출력 예시:
PID CMD
1 sleep infinity
12 /bin/bash
23 ps -Ao pid,cmd
프로세스 분석:
컨테이너 프로세스 구조:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PID 1: sleep infinity
└─ hello.sh에서 실행한 메인 프로세스
└─ 컨테이너의 생명주기를 유지
PID 12: /bin/bash
└─ docker exec로 실행한 쉘
└─ 임시 프로세스
PID 23: ps -Ao pid,cmd
└─ 현재 실행한 명령어
└─ 일회성 프로세스
특징:
→ systemd, init 같은 데몬 없음
→ 단일 프로세스 중심 실행
→ PID 1이 종료되면 컨테이너 종료
용어 정리
- docker run: 이미지로부터 컨테이너를 생성하고 실행하는 명령어
- docker exec: 실행 중인 컨테이너에서 추가 명령어를 실행하는 명령어
- -it 옵션: -i(인터랙티브 모드)와 -t(TTY 할당)를 조합. 쉘 접속 시 사용
- PID 1: 컨테이너의 메인 프로세스. 이 프로세스가 종료되면 컨테이너도 종료됨
- 쓰기 가능 레이어: 컨테이너 생성 시 이미지 위에 추가되는 레이어. 컨테이너의 모든 변경 사항 저장
컨테이너 생명주기 관리
기본 관리 명령어:
# 컨테이너 중지
docker stop mycontainer
# 컨테이너 시작
docker start mycontainer
# 컨테이너 재시작
docker restart mycontainer
# 컨테이너 삭제
docker rm mycontainer
# 실행 중인 컨테이너 강제 삭제
docker rm -f mycontainer
컨테이너 상태 확인:
# 실행 중인 컨테이너만 표시
docker ps
# 모든 컨테이너 표시 (중지된 것 포함)
docker ps -a
# 컨테이너 로그 확인
docker logs mycontainer
# 컨테이너 상세 정보
docker inspect mycontainer
컨테이너 상태 전이:
컨테이너 상태 다이어그램:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[이미지]
↓ docker run
[Created]
↓ 자동 시작
[Running] ←─────┐
│ │ docker start
↓ docker stop │
[Stopped] ──────┘
↓ docker rm
[Deleted]
주요 명령어:
├─ run: 이미지 → 생성 → 실행
├─ stop: 실행 → 중지
├─ start: 중지 → 실행
├─ restart: 재시작
└─ rm: 삭제
백그라운드 실행
detached 모드:
# 백그라운드에서 실행 (-d 옵션)
docker run -d --name mycontainer myimage:v1
# 로그 확인
docker logs mycontainer
# 실시간 로그 스트리밍
docker logs -f mycontainer
포그라운드 vs 백그라운드:
실행 모드 비교:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[포그라운드 모드]
docker run --name mycontainer myimage:v1
↓
터미널에 출력 표시
Ctrl+C로 종료 가능
터미널 점유
[백그라운드 모드 (Detached)]
docker run -d --name mycontainer myimage:v1
↓
컨테이너 ID만 출력
터미널 즉시 반환
백그라운드 실행 계속
사용 시나리오:
→ 포그라운드: 테스트, 디버깅
→ 백그라운드: 서비스 운영, 데몬
용어 정리
- -d 옵션 (Detached): 컨테이너를 백그라운드에서 실행하는 옵션. 터미널을 점유하지 않음
- docker logs: 컨테이너의 표준 출력(stdout)과 표준 에러(stderr)를 확인하는 명령어
- docker stop/start: 컨테이너를 중지하거나 다시 시작하는 명령어
- docker rm: 중지된 컨테이너를 삭제하는 명령어. -f 옵션으로 실행 중인 컨테이너도 강제 삭제 가능
2.1.3. Ship: 레지스트리를 사용한 컨테이너 배포
레지스트리 개념
이미지 배포 메커니즘:
레지스트리 기반 배포:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[개발 환경]
↓ docker build
[로컬 이미지]
↓ docker push
[레지스트리 (Docker Hub)]
│
├─ docker pull → [테스트 환경]
├─ docker pull → [스테이징 환경]
└─ docker pull → [프로덕션 환경]
장점:
✓ 중앙 집중식 관리
✓ 버전 관리
✓ 팀 간 공유
✓ 환경 간 이식성
레지스트리 유형:
레지스트리 종류:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[퍼블릭 레지스트리]
├─ Docker Hub
│ └─ hub.docker.com
│ └─ 무료 퍼블릭 리포지터리
│
├─ GitHub Container Registry (ghcr.io)
│ └─ ghcr.io/{username}/{image}
│
├─ Quay.io
│ └─ Red Hat 제공
│
└─ Google Container Registry (gcr.io)
└─ GCP 통합
[프라이빗 레지스트리]
├─ Docker Hub Private
├─ AWS ECR (Elastic Container Registry)
├─ Azure Container Registry
├─ Harbor (오픈소스)
└─ 자체 구축 Registry
용어 정리
- 레지스트리 (Registry): 컨테이너 이미지를 저장하고 배포하는 중앙 저장소
- Docker Hub: Docker의 공식 퍼블릭 레지스트리. hub.docker.com
- docker push: 로컬 이미지를 레지스트리에 업로드하는 명령어
- docker pull: 레지스트리에서 이미지를 다운로드하는 명령어
- 퍼블릭/프라이빗 레지스트리: 공개 접근 가능 여부에 따른 레지스트리 분류
Docker Hub 사용하기
리포지터리와 태그 구조:
이미지 이름 체계:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[로컬 이미지]
myimage:v1
│ │
│ └─ 태그 (버전)
│
└─ 리포지터리 이름
[Docker Hub 이미지]
username/myimage:v1
│ │ │
│ │ └─ 태그
│ │
│ └─ 리포지터리 이름
│
└─ 사용자명 (네임스페이스)
[다른 레지스트리]
ghcr.io/username/myimage:v1
│ │ │ │
│ │ │ └─ 태그
│ │ │
│ │ └─ 리포지터리 이름
│ │
│ └─ 사용자명
│
└─ 레지스트리 주소
규칙:
→ 레지스트리 생략 시 Docker Hub 기본
→ 사용자명 생략 시 공식 이미지
→ 태그 생략 시 latest 기본
용어 정리
- 이미지 이름 체계:
[레지스트리/]사용자명/리포지터리:태그형식 - 네임스페이스: Docker Hub에서 사용자명이나 조직명으로 이미지를 구분하는 단위
- 공식 이미지 (Official Image): Docker가 검증한 신뢰할 수 있는 이미지. 사용자명 없이 바로 사용 (예: ubuntu, nginx)
- latest 태그: 태그 미지정 시 기본으로 사용되는 태그. 최신 버전을 의미하지만 명시적 버전 사용 권장
Docker Hub 배포 실습
1. Docker Hub 로그인:
docker login
로그인 과정:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Username: username
Password: ********
↓
Login Succeeded
↓
자격 증명 저장:
~/.docker/config.json
2. 이미지 태깅:
docker tag myimage:v1 username/myimage:v1
태깅 과정 설명:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[원본 이미지]
myimage:v1
↓ docker tag
[새 태그 생성]
username/myimage:v1
특징:
→ 동일한 이미지에 다른 이름 부여
→ 실제 복사 없음 (참조만 생성)
→ 저장 공간 추가 사용 안 함
3. 이미지 확인:
docker image ls username/myimage:v1
출력 예시:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
REPOSITORY TAG IMAGE ID CREATED SIZE
username/myimage v1 abc123def 5 mins ago 117MB
확인:
→ myimage:v1과 IMAGE ID 동일
→ 같은 이미지를 가리키는 다른 이름
4. 이미지 푸시 (업로드):
docker push username/myimage:v1
푸시 과정:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[로컬 이미지]
username/myimage:v1
↓
[레이어별 업로드]
├─ Layer 1: ubuntu:22.04 (스킵 - 이미 존재)
├─ Layer 2: hello.sh 복사 (업로드)
├─ Layer 3: chmod 실행 (업로드)
└─ Layer 4: ENTRYPOINT (업로드)
↓
[Docker Hub]
https://hub.docker.com/r/username/myimage
최적화:
→ 공통 레이어는 재사용
→ 새 레이어만 업로드
→ 네트워크 효율성 증대
5. 이미지 풀 (다운로드):
# 다른 환경에서 이미지 다운로드
docker pull username/myimage:v1
풀 과정:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[Docker Hub]
username/myimage:v1
↓
[레이어별 다운로드]
├─ Layer 1: ubuntu:22.04 (다운로드)
├─ Layer 2: hello.sh (다운로드)
├─ Layer 3: chmod (다운로드)
└─ Layer 4: ENTRYPOINT (다운로드)
↓
[로컬 이미지]
username/myimage:v1
확인:
→ 모든 레이어 다운로드 완료
→ 어디서든 동일한 환경 구성
6. 다운로드한 이미지 확인:
docker images
7. 새 컨테이너 실행:
docker run --name mycontainer2 username/myimage:v1
배포 시나리오
실무 배포 흐름:
CI/CD 파이프라인 예시:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[개발 환경]
↓
코드 커밋 → Git
↓
[CI 서버]
↓
1. 코드 체크아웃
2. docker build -t myapp:${VERSION}
3. 테스트 실행
4. docker tag myapp:${VERSION} registry.com/myapp:${VERSION}
5. docker push registry.com/myapp:${VERSION}
↓
[레지스트리]
registry.com/myapp:1.0.0
registry.com/myapp:1.0.1
registry.com/myapp:latest
↓
[배포]
├─→ 테스트 환경
│ docker pull registry.com/myapp:1.0.1
│ docker run myapp:1.0.1
│
├─→ 스테이징 환경
│ 검증 후 승인
│
└─→ 프로덕션 환경
Kubernetes 배포
또는 docker-compose
태깅 전략:
├─ ${VERSION}: 특정 버전 (1.0.0, 1.0.1)
├─ latest: 최신 안정 버전
├─ develop: 개발 브랜치
└─ ${GIT_SHA}: 커밋 해시
핵심 요약
도커 작업 흐름 정리:
Build, Ship, Run 요약:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[Build - 이미지 작성]
├─ Dockerfile 작성
│ └─ 컨테이너 구성 명령 정의
│
├─ docker build -t 이미지명:태그 경로
│ └─ 레이어별 이미지 생성
│
└─ 결과: 재사용 가능한 이미지
└─ 읽기 전용 템플릿
[Ship - 이미지 배포]
├─ docker tag 로컬이미지 레지스트리/이미지
│ └─ 배포용 태그 부여
│
├─ docker push 레지스트리/이미지
│ └─ 레지스트리에 업로드
│
├─ docker pull 레지스트리/이미지
│ └─ 레지스트리에서 다운로드
│
└─ 결과: 환경 간 이식성 확보
└─ 동일한 이미지 공유
[Run - 컨테이너 실행]
├─ docker run 이미지명
│ └─ 컨테이너 생성 및 실행
│
├─ docker exec -it 컨테이너 명령어
│ └─ 실행 중인 컨테이너 접속
│
├─ docker logs 컨테이너
│ └─ 로그 확인
│
└─ 결과: 격리된 실행 환경
└─ 독립적인 프로세스 공간
핵심 개념:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[이미지]
→ 읽기 전용 템플릿
→ 레이어 구조
→ 재사용 가능
[컨테이너]
→ 이미지의 실행 인스턴스
→ 쓰기 가능 레이어 추가
→ 독립된 프로세스 공간
[레지스트리]
→ 이미지 저장소
→ 버전 관리
→ 팀 간 공유
결론:
→ Build: 한 번 작성
→ Ship: 어디든 배포
→ Run: 동일하게 실행
→ 컨테이너의 이식성 극대화
참고 자료
공식 문서:
- Dockerfile Reference: https://docs.docker.com/engine/reference/builder/
- Docker Build: https://docs.docker.com/build/
- Docker Hub: https://hub.docker.com/
베스트 프랙티스:
- Dockerfile Best Practices: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
- Docker Image Build Guide: https://docs.docker.com/build/guide/